* didn't add some files in last commit ...
This commit is contained in:
parent
a547c7e7e9
commit
ffc6a8b931
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
|
|
||||||
|
import static java.awt.BasicStroke.*;
|
||||||
|
import static java.awt.RenderingHints.*;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Insets;
|
||||||
|
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
|
||||||
|
|
||||||
|
class DashedSeparator implements Border {
|
||||||
|
|
||||||
|
private final int height;
|
||||||
|
private final int dash;
|
||||||
|
|
||||||
|
private final Color foreground;
|
||||||
|
private final Color background;
|
||||||
|
|
||||||
|
|
||||||
|
public DashedSeparator(int height, int dash, Color foreground, Color background) {
|
||||||
|
this.height = height;
|
||||||
|
this.dash = dash;
|
||||||
|
this.foreground = foreground;
|
||||||
|
this.background = background;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Insets getBorderInsets(Component c) {
|
||||||
|
return new Insets(0, 0, height, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBorderOpaque() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
|
||||||
|
Graphics2D g2d = (Graphics2D) g.create(x, h - this.height, w, h);
|
||||||
|
|
||||||
|
// fill background
|
||||||
|
g2d.setPaint(background);
|
||||||
|
g2d.fillRect(0, 0, w, h);
|
||||||
|
|
||||||
|
// draw dashed line
|
||||||
|
g2d.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
|
||||||
|
g2d.setColor(foreground);
|
||||||
|
g2d.setStroke(new BasicStroke(1, CAP_ROUND, JOIN_ROUND, 1, new float[] { dash }, 0));
|
||||||
|
|
||||||
|
g2d.drawLine(dash, this.height / 2, w - dash, this.height / 2);
|
||||||
|
|
||||||
|
g2d.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
|
||||||
|
import javax.swing.DefaultListCellRenderer;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageComboBoxCellRenderer extends DefaultListCellRenderer {
|
||||||
|
|
||||||
|
private final Border padding = new EmptyBorder(2, 2, 2, 2);
|
||||||
|
|
||||||
|
private final Border favoritePadding = new EmptyBorder(0, 6, 0, 6);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||||
|
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
|
|
||||||
|
Language language = (Language) value;
|
||||||
|
setText(language.getName());
|
||||||
|
setIcon(ResourceManager.getFlagIcon(language.getCode()));
|
||||||
|
|
||||||
|
// default padding
|
||||||
|
setBorder(padding);
|
||||||
|
|
||||||
|
LanguageComboBoxModel model = (LanguageComboBoxModel) list.getModel();
|
||||||
|
|
||||||
|
if ((index > 0 && index <= model.favorites().size())) {
|
||||||
|
// add favorite padding
|
||||||
|
setBorder(new CompoundBorder(favoritePadding, getBorder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == 0 || index == model.favorites().size()) {
|
||||||
|
// add separator border
|
||||||
|
setBorder(new CompoundBorder(new DashedSeparator(10, 4, Color.lightGray, list.getBackground()), getBorder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
|
||||||
|
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
|
|
||||||
|
import static net.sourceforge.filebot.ui.panel.subtitle.Language.*;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.swing.AbstractListModel;
|
||||||
|
import javax.swing.ComboBoxModel;
|
||||||
|
|
||||||
|
|
||||||
|
class LanguageComboBoxModel extends AbstractListModel implements ComboBoxModel {
|
||||||
|
|
||||||
|
public static final Language ALL_LANGUAGES = new Language("undefined", "All Languages");
|
||||||
|
|
||||||
|
private Language selection = ALL_LANGUAGES;
|
||||||
|
|
||||||
|
private List<Language> favorites = new Favorites(2);
|
||||||
|
|
||||||
|
private List<Language> values;
|
||||||
|
|
||||||
|
|
||||||
|
public LanguageComboBoxModel() {
|
||||||
|
values = Arrays.asList(availableLanguages());
|
||||||
|
|
||||||
|
// sort languages by name
|
||||||
|
Collections.sort(values, ALPHABETIC_ORDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Language getElementAt(int index) {
|
||||||
|
// "All Languages"
|
||||||
|
if (index == 0) {
|
||||||
|
return ALL_LANGUAGES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "All Languages" offset
|
||||||
|
index -= 1;
|
||||||
|
|
||||||
|
if (index < favorites.size()) {
|
||||||
|
return favorites.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Favorites" offset
|
||||||
|
index -= favorites.size();
|
||||||
|
|
||||||
|
return values.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
// "All Languages" : favorites[] : values[]
|
||||||
|
return 1 + favorites.size() + values.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<Language> favorites() {
|
||||||
|
return favorites;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Language getSelectedItem() {
|
||||||
|
return selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSelectedItem(Object value) {
|
||||||
|
if (value instanceof Language) {
|
||||||
|
selection = (Language) value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected int convertFavoriteIndexToModel(int favoriteIndex) {
|
||||||
|
return 1 + favoriteIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void fireFavoritesAdded(int from, int to) {
|
||||||
|
fireIntervalAdded(this, convertFavoriteIndexToModel(from), convertFavoriteIndexToModel(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void fireFavoritesRemoved(int from, int to) {
|
||||||
|
fireIntervalRemoved(this, convertFavoriteIndexToModel(from), convertFavoriteIndexToModel(to));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class Favorites extends AbstractList<Language> implements Set<Language> {
|
||||||
|
|
||||||
|
private final List<Language> data;
|
||||||
|
|
||||||
|
private final int capacity;
|
||||||
|
|
||||||
|
|
||||||
|
public Favorites(int capacity) {
|
||||||
|
this.data = new ArrayList<Language>(capacity);
|
||||||
|
this.capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Language get(int index) {
|
||||||
|
return data.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean add(Language element) {
|
||||||
|
// add first
|
||||||
|
return addIfAbsent(0, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void add(int index, Language element) {
|
||||||
|
addIfAbsent(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean addIfAbsent(int index, Language element) {
|
||||||
|
// 1. ignore null values
|
||||||
|
// 2. ignore ALL_LANGUAGES
|
||||||
|
// 3. make sure there are no duplicates
|
||||||
|
// 4. limit size to capacity
|
||||||
|
if (element == null || element == ALL_LANGUAGES || contains(element) || index >= capacity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure there is free space
|
||||||
|
if (data.size() >= capacity) {
|
||||||
|
// remove last
|
||||||
|
remove(data.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add clone of language, because KeySelection behaviour will
|
||||||
|
// get weird if the same object is in the model multiple times
|
||||||
|
data.add(index, element.clone());
|
||||||
|
|
||||||
|
// update view
|
||||||
|
fireFavoritesAdded(index, index);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object obj) {
|
||||||
|
// check via language code, because data consists of clones
|
||||||
|
if (obj instanceof Language) {
|
||||||
|
Language language = (Language) obj;
|
||||||
|
|
||||||
|
for (Language element : data) {
|
||||||
|
if (language.getCode().equals(element.getCode()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Language remove(int index) {
|
||||||
|
Language old = data.remove(index);
|
||||||
|
|
||||||
|
// update view
|
||||||
|
fireFavoritesRemoved(index, index);
|
||||||
|
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return data.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
|
|
||||||
|
import static java.awt.event.ItemEvent.*;
|
||||||
|
|
||||||
|
import java.awt.event.ItemEvent;
|
||||||
|
import java.awt.event.ItemListener;
|
||||||
|
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
|
||||||
|
|
||||||
|
class PopupSelectionListener implements PopupMenuListener, ItemListener {
|
||||||
|
|
||||||
|
private Object selected = null;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
|
||||||
|
JComboBox comboBox = (JComboBox) e.getSource();
|
||||||
|
|
||||||
|
// selected item before popup
|
||||||
|
selected = comboBox.getSelectedItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
|
||||||
|
JComboBox comboBox = (JComboBox) e.getSource();
|
||||||
|
|
||||||
|
// check selected item after popup
|
||||||
|
if (selected != comboBox.getSelectedItem()) {
|
||||||
|
itemStateChanged(new ItemEvent(comboBox, ITEM_STATE_CHANGED, comboBox.getSelectedItem(), SELECTED));
|
||||||
|
}
|
||||||
|
|
||||||
|
selected = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuCanceled(PopupMenuEvent e) {
|
||||||
|
selected = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(ItemEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
|
||||||
|
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JProgressBar;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
|
import net.sourceforge.tuned.ui.AbstractFancyListCellRenderer;
|
||||||
|
|
||||||
|
|
||||||
|
public class SubtitleListCellRenderer extends AbstractFancyListCellRenderer {
|
||||||
|
|
||||||
|
private final JLabel titleLabel = new JLabel();
|
||||||
|
private final JLabel languageLabel = new JLabel();
|
||||||
|
|
||||||
|
private final JProgressBar progressBar = new JProgressBar(0, 100);
|
||||||
|
|
||||||
|
private final Map<String, String> languageCodeMap = mapLanguageCodeByName(Language.availableLanguages());
|
||||||
|
|
||||||
|
|
||||||
|
public SubtitleListCellRenderer() {
|
||||||
|
setHighlightingEnabled(false);
|
||||||
|
|
||||||
|
progressBar.setStringPainted(true);
|
||||||
|
progressBar.setOpaque(false);
|
||||||
|
progressBar.setPreferredSize(new Dimension(80, 18));
|
||||||
|
|
||||||
|
setLayout(new MigLayout("fill, nogrid, insets 0"));
|
||||||
|
|
||||||
|
add(languageLabel, "hidemode 3, w 85px!");
|
||||||
|
add(titleLabel, "");
|
||||||
|
add(progressBar, "gap indent:push");
|
||||||
|
|
||||||
|
setBorder(new CompoundBorder(new DashedSeparator(2, 4, Color.lightGray, Color.white), getBorder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||||
|
super.configureListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
|
|
||||||
|
SubtitlePackage subtitle = (SubtitlePackage) value;
|
||||||
|
|
||||||
|
titleLabel.setIcon(ResourceManager.getIcon("status.archive"));
|
||||||
|
titleLabel.setText(subtitle.getName());
|
||||||
|
|
||||||
|
if (languageLabel.isVisible()) {
|
||||||
|
languageLabel.setText(subtitle.getLanguageName());
|
||||||
|
languageLabel.setIcon(ResourceManager.getFlagIcon(languageCodeMap.get(subtitle.getLanguageName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO download + progress
|
||||||
|
progressBar.setVisible(false);
|
||||||
|
progressBar.setString(subtitle.getDownloadTask().getState().toString().toLowerCase());
|
||||||
|
|
||||||
|
titleLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
|
||||||
|
languageLabel.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
|
||||||
|
|
||||||
|
// don't paint border on last element
|
||||||
|
setBorderPainted(index < list.getModel().getSize() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> mapLanguageCodeByName(Language[] languages) {
|
||||||
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
|
|
||||||
|
for (Language language : languages) {
|
||||||
|
map.put(language.getName(), language.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JLabel getLanguageLabel() {
|
||||||
|
return languageLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() {
|
||||||
|
// validate children, yet avoid flickering of the mouse cursor
|
||||||
|
validateTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
package net.sourceforge.filebot.ui.panel.subtitle;
|
||||||
|
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JList;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import net.sourceforge.filebot.ResourceManager;
|
||||||
|
import ca.odell.glazedlists.BasicEventList;
|
||||||
|
import ca.odell.glazedlists.EventList;
|
||||||
|
import ca.odell.glazedlists.FilterList;
|
||||||
|
import ca.odell.glazedlists.GlazedLists;
|
||||||
|
import ca.odell.glazedlists.ListSelection;
|
||||||
|
import ca.odell.glazedlists.TextFilterator;
|
||||||
|
import ca.odell.glazedlists.swing.EventListModel;
|
||||||
|
import ca.odell.glazedlists.swing.EventSelectionModel;
|
||||||
|
import ca.odell.glazedlists.swing.TextComponentMatcherEditor;
|
||||||
|
|
||||||
|
|
||||||
|
public class SubtitleListComponent extends JComponent {
|
||||||
|
|
||||||
|
private EventList<SubtitlePackage> model = new BasicEventList<SubtitlePackage>();
|
||||||
|
|
||||||
|
private SubtitleListCellRenderer renderer = new SubtitleListCellRenderer();
|
||||||
|
|
||||||
|
private JTextField filterEditor = new JTextField();
|
||||||
|
|
||||||
|
|
||||||
|
public SubtitleListComponent() {
|
||||||
|
TextFilterator<SubtitlePackage> filterator = GlazedLists.toStringTextFilterator();
|
||||||
|
TextComponentMatcherEditor<SubtitlePackage> matcherEditor = new TextComponentMatcherEditor<SubtitlePackage>(filterEditor, filterator);
|
||||||
|
|
||||||
|
JList list = new JList(new EventListModel<SubtitlePackage>(new FilterList<SubtitlePackage>(model, matcherEditor)));
|
||||||
|
list.setCellRenderer(renderer);
|
||||||
|
list.setFixedCellHeight(35);
|
||||||
|
|
||||||
|
EventSelectionModel<SubtitlePackage> selectionModel = new EventSelectionModel<SubtitlePackage>(model);
|
||||||
|
selectionModel.setSelectionMode(ListSelection.MULTIPLE_INTERVAL_SELECTION_DEFENSIVE);
|
||||||
|
list.setSelectionModel(selectionModel);
|
||||||
|
|
||||||
|
JButton clearButton = new JButton(clearFilterAction);
|
||||||
|
clearButton.setOpaque(false);
|
||||||
|
|
||||||
|
setLayout(new MigLayout("fill, nogrid", "[fill]", "[pref!][fill]"));
|
||||||
|
|
||||||
|
add(new JLabel("Filter:"), "gap indent:push");
|
||||||
|
add(filterEditor, "wmin 120px, gap rel");
|
||||||
|
add(clearButton, "w 24px!, h 24px!");
|
||||||
|
add(new JScrollPane(list), "newline");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public EventList<SubtitlePackage> getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setLanguageVisible(boolean visible) {
|
||||||
|
renderer.getLanguageLabel().setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Action clearFilterAction = new AbstractAction(null, ResourceManager.getIcon("edit.clear")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
filterEditor.setText("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<languages>
|
||||||
|
<language code="sq" name="Albanian" />
|
||||||
|
<language code="ar" name="Arabic" />
|
||||||
|
<language code="hy" name="Armenian" />
|
||||||
|
<language code="pb" name="Brazilian" />
|
||||||
|
<language code="bg" name="Bulgarian" />
|
||||||
|
<language code="ca" name="Catalan" />
|
||||||
|
<language code="zh" name="Chinese" />
|
||||||
|
<language code="hr" name="Croatian" />
|
||||||
|
<language code="cs" name="Czech" />
|
||||||
|
<language code="da" name="Danish" />
|
||||||
|
<language code="nl" name="Dutch" />
|
||||||
|
<language code="en" name="English" />
|
||||||
|
<language code="et" name="Estonian" />
|
||||||
|
<language code="fi" name="Finnish" />
|
||||||
|
<language code="fr" name="French" />
|
||||||
|
<language code="de" name="German" />
|
||||||
|
<language code="el" name="Greek" />
|
||||||
|
<language code="he" name="Hebrew" />
|
||||||
|
<language code="hi" name="Hindi" />
|
||||||
|
<language code="hu" name="Hungarian" />
|
||||||
|
<language code="id" name="Indonesian" />
|
||||||
|
<language code="it" name="Italian" />
|
||||||
|
<language code="ja" name="Japanese" />
|
||||||
|
<language code="ko" name="Korean" />
|
||||||
|
<language code="lv" name="Latvian" />
|
||||||
|
<language code="lt" name="Lithuanian" />
|
||||||
|
<language code="mk" name="Macedonian" />
|
||||||
|
<language code="ms" name="Malay" />
|
||||||
|
<language code="no" name="Norwegian" />
|
||||||
|
<language code="fa" name="Persian" />
|
||||||
|
<language code="pl" name="Polish" />
|
||||||
|
<language code="pt" name="Portuguese" />
|
||||||
|
<language code="ro" name="Romanian" />
|
||||||
|
<language code="ru" name="Russian" />
|
||||||
|
<language code="sr" name="Serbian" />
|
||||||
|
<language code="sk" name="Slovak" />
|
||||||
|
<language code="sl" name="Slovenian" />
|
||||||
|
<language code="es" name="Spanish" />
|
||||||
|
<language code="sv" name="Swedish" />
|
||||||
|
<language code="th" name="Thai" />
|
||||||
|
<language code="tr" name="Turkish" />
|
||||||
|
<language code="vi" name="Vietnamese" />
|
||||||
|
</languages>
|
Loading…
Reference in New Issue
Block a user