Initial commit of scripts for importing & exporting translations from weblate

This commit is contained in:
Colin Basnett
2024-03-19 01:08:21 -07:00
parent d48534f1a5
commit 56c847e791
6 changed files with 290 additions and 94 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "extern/io_scene_psk_psa-translations"]
path = extern/io_scene_psk_psa-translations
url = https://github.com/DarklightGames/io_scene_psk_psa-translations.git

View File

@@ -56,6 +56,19 @@ Bug fixes will be issued for legacy addon versions that are under [Blender's LTS
> Note that in order to see the imported actions applied to your armature, you must use the [Dope Sheet](https://docs.blender.org/manual/en/latest/editors/dope_sheet/introduction.html) or [Nonlinear Animation](https://docs.blender.org/manual/en/latest/editors/nla/introduction.html) editors. > Note that in order to see the imported actions applied to your armature, you must use the [Dope Sheet](https://docs.blender.org/manual/en/latest/editors/dope_sheet/introduction.html) or [Nonlinear Animation](https://docs.blender.org/manual/en/latest/editors/nla/introduction.html) editors.
## Localization
The following languages are currently supported:
| Language | Status | Method |
|----------------|--------|--------|
| English | ✅️ | 🧑 |
| Japanese (日本語) | ✅️ | 🤖 |
If you notice any issues with the translations, please feel free to submit a correction on [Weblate](https://weblate.darklightgames.com/projects/io_scene_psk_psa/).
If you would like to see support add for your own native language, please [open an issue](http://github.com/DarklightGames/io_scene_psk_psa/issues) and we will consider adding it.
# FAQ # FAQ
## Why can't I see the animations imported from my PSA? ## Why can't I see the animations imported from my PSA?

View File

@@ -1,50 +1,201 @@
langs = { langs = {'ja': {('*', 'Action Metadata'): 'アクションメタデータ',
'ja_JP': { ('*', 'Additional data will be written to the properties of the Action (e.g., frame rate)'): 'アクションのプロパティに追加データが書き込まれます '
('*', 'Bone Length'): 'ボーンの長さ', '(フレームレートなど)',
('*', 'Extra UVs'): '追加のUV',
('*', 'Vertex Color Space'): '頂点カラーの色空間',
('*', 'Dependencies'): '依存関係',
('*', 'All dependencies are installed'): 'すべての依存関係がインストールされています',
('*', 'Reinstall'): '再インストール',
('*', 'Root Motion'): 'ルートモーション',
('*', 'Action Metadata'): 'アクションのメタデータ', # TODO: this should probably be removed in favor of directly using the values in the property groups
('*', 'PSA Export'): 'PSAエクスポート',
('*', 'Compression Ratio'): '圧縮率',
('*', 'Keyframe Quota'): 'キーフレームのクォータ',
('*', 'The minimum number of keyframes to be exported'): 'エクスポートするキーフレームの最小数',
('*', 'Select a PSA file'): 'PSAファイルを選択',
('*', 'Case Insensitive'): '大文字小文字を区別しない',
('*', 'All bones will be exported'): 'すべてのボーンがエクスポートされます', ('*', 'All bones will be exported'): 'すべてのボーンがエクスポートされます',
('*', 'Only bones belonging to the selected bone collections and their ancestors will be exported'): ('*', 'All selected meshes must have the same armature modifier, encountered {count} ({names})'): '選択したすべてのメッシュには同じアーマチュア修飾子が必要です。検出されたアーマチュア修飾子は '
'選択したボーングループとその祖先に属するボーンのみがエクスポートされます', '{count} '
('*', 'Raw Mesh Data'): '生のメッシュデータ', '({names})',
('*', 'At least one bone must be marked for export'): '少なくとも 1 つのボーンをエクスポート用にマークする必要があります', ('*', 'An armature must be selected'): 'アーマチュアを選択する必要があります',
('*', 'Number of wedges ({wedge_count}) exceeds limit of {max_wedge_count}'): 'ウェッジの数({wedge_count})が{max_wedge_count}の制限を超えています', ('*', 'An armature object must be supplied'): 'アーマチュアオブジェクトを指定する必要があります',
('*', 'Numbers of vertices ({point_count}) exceeds limit of {max_point_count}'): '頂点の数({point_count})が{max_point_count}の制限を超えています', ('*', 'Assign each imported action a fake user so that the data block is always saved'): 'インポートされた各アクションにフェイクユーザーを割り当てて、データブロックが常に保存されるようにします',
('*', 'Number of materials ({material_count}) exceeds limit of {max_material_count}'): 'マテリアルの数({material_count})が{max_material_count}の制限を超えています', ('*', 'At least one bone must be marked for export'): '少なくとも 1 '
('*', 'Number of bones ({bone_count}) exceeds limit of {max_bone_count}'): 'ボーンの数({bone_count})が{max_bone_count}の制限を超えています', 'つのボーンをエクスポート対象としてマークする必要があります',
('*', 'Load a PSK file'): 'PSKファイルを読み込む', ('*', 'At least one mesh must be selected'): '少なくとも 1 '
('*', 'The active object must be an armature'): 'アクティブなオブジェクトはアーマチュアである必要があります', 'つのメッシュを選択する必要があります',
('*', 'Import the selected animations into the scene as actions'): '選択したアニメーションをアクションとしてシーンにインポートします', ('*', 'Bone Collections'): 'ボーンコレクション',
('*', 'Import'): 'インポート', ('*', 'Bone Filter'): 'ボーンフィルター',
('*', 'Import extra UVs, if available'): '利用可能な場合、追加のUVをインポートします', ('*', 'Bone Length'): 'ボーンの長さ',
('*', 'Import vertex normals, if available'): '利用可能な場合、頂点法線をインポートします', ('*', 'Bone Name Mapping'): 'ボーン名マッピング',
('*', 'Import vertex colors, if available'): '利用可能な場合、頂点カラーをインポートします', ('*', 'Bone name "{name}" contains characters that cannot be encoded in the Windows-1252 codepage'): 'ボーン名「{name}」には、Windows-1252 '
('*', 'Import shape keys, if available'): '利用可能な場合、シェイプキーをインポートします', 'コードページではエンコードできない文字が含まれています',
('*', 'The source vertex color space'): 'ソースの頂点カラーの色空間', ('*', 'Bone names must match exactly'): 'ボーン名は正確に一致する必要があります',
('*', 'Unhandled section "{section_name}" at position {position}') : '位置{position}"{section_name}"セクションは処理されていません', ('*', 'Bone names restrictions will be enforced. Note that bone names without properly formatted names may not be able to be referenced by some versions of the Unreal Engine'): 'ボーン名の制限が適用されます。ボーン名が正しくフォーマットされていないと、Unreal '
('*', 'Nothing to import'): 'インポートするものがありません', 'Engine '
('*', 'PSK imported with {count} warning(s)'): '{count}個の警告付きでPSKがインポートされました', 'のバージョンによっては参照できない場合があることに注意してください',
('*', 'PSK imported ({name})'): 'PSKがインポートされました({name})', ('*', 'Bones'): 'ボーンズ',
('*', 'FPS Source'): 'FPSのソース', ('*', "Bones names must match, ignoring case (e.g., the PSA bone 'aBcDeF' can be mapped to the armature bone 'ABCDEF')"): 'ボーンの名前は大文字と小文字を区別しないで一致する必要があります '
('*', 'Prefix Action Name'): 'アクション名のプレフィックス', '(例:PSAボーン '
"'abcDef' "
'はアーマチュアボーン '
"'ABCDEF' "
'にマッピングできます)',
('*', 'Case Insensitive'): '大文字と小文字を区別しない',
('*', 'Compression Ratio'): '圧縮率',
('*', 'Context'): 'コンテキスト',
('*', 'Convert keyframes to read-only samples. Recommended if you do not plan on editing the actions directly'): 'キーフレームを読み取り専用サンプルに変換します。アクションを直接編集する予定がない場合におすすめです',
('*', 'Convert to Samples'): 'サンプルに変換', ('*', 'Convert to Samples'): 'サンプルに変換',
('*', 'Stash'): '保留', ('*', 'Custom FPS'): 'カスタム FPS',
('*', 'Select all visible sequences'): 'すべての表示されているシーケンスを選択', ('*', 'Deselect All'): 'すべて選択解除',
('*', 'Deselect all visible sequences'): 'すべての表示されているシーケンスの選択解除', ('*', 'Deselect all bone collections'): 'すべてのボーンコレクションを選択解除',
('*', 'Deselect all visible sequences'): '表示されているシーケンスをすべて選択解除',
('*', 'Discarded {count} invalid face(s)'): '{count} 個の無効なフェースが廃棄されました',
('*', 'Each sequence name should be on a new line'): '各シーケンス名は新しい行にする必要があります',
('*', 'Enforce Bone Name Restrictions'): 'ボーン名制限を強制',
('*', 'Enforce that bone names must only contain letters, numbers, spaces, hyphens and underscores.\n\nDepending on the engine, improper bone names might not be referenced correctly by scripts'): 'ボーン名には文字、数字、スペース、ハイフン、アンダースコアのみを含めるようにしてください。\n'
'\n'
'エンジンによっては、不適切なボーン名がスクリプトで正しく参照されない場合があります',
('*', 'Exact'): '正確',
('*', 'Export'): 'エクスポート',
('*', 'Export actions to PSA'): 'アクションを PSA にエクスポート',
('*', 'Export mesh and armature to PSK'): 'メッシュとアーマチュアを PSK にエクスポート',
('*', 'Extra UVs'): '追加のUV',
('*', 'FPS Source'): 'FPS ソース',
('*', 'Failed to read PSA config file: {error}'): 'PSA '
'構成ファイルを読み込めませんでした:{error}',
('*', 'Fake User'): 'フェイクユーザー',
('*', 'File Path'): 'ファイルパス',
('*', 'File path used for exporting the PSA file'): 'PSA '
'ファイルのエクスポートに使用するファイルパス',
('*', 'File path used for exporting the PSK file'): 'PSK '
'ファイルのエクスポートに使用するファイルパス',
('*', 'File path used for importing the PSA file'): 'PSA '
'ファイルのインポートに使用するファイルパス',
('*', 'Filter by Name'): '名前で絞り込む',
('*', 'Filter using regular expressions'): '正規表現を使用してフィルタリングする',
('*', 'Flags'): '国旗',
('*', "If an action with a matching name already exists, the existing action will have it's data overwritten instead of a new action being created"): '一致する名前のアクションが既に存在する場合、新しいアクションが作成されるのではなく、既存のアクションのデータが上書きされます',
('*', 'Import'): 'インポート',
('*', 'Import Extra UVs'): '追加の UV をインポート',
('*', 'Import Shape Keys'): 'シェイプキーをインポート',
('*', 'Import Vertex Colors'): '頂点カラーをインポート',
('*', 'Import Vertex Normals'): '頂点法線をインポート',
('*', 'Import extra UVs, if available'): '追加の UV (可能な場合) をインポートする',
('*', 'Import shape keys, if available'): 'シェイプキーをインポートする (可能な場合)',
('*', 'Import the selected animations into the scene as actions'): '選択したアニメーションをアクションとしてシーンにインポートします',
('*', 'Import vertex colors, if available'): '頂点カラーをインポート (可能な場合)',
('*', 'Import vertex normals, if available'): '頂点法線 (可能な場合) をインポートする',
('*', 'Invert filtering (show hidden items, and vice versa)'): '反転フィルタリング '
'(隠しアイテムを表示、その逆)',
('*', 'Keyframe Quota'): 'キーフレームクォータ',
('*', 'Load a PSK file'): 'PSK ファイルを読み込む',
('*', 'Material name "{name}" contains characters that cannot be encoded in the Windows-1252 codepage'): 'マテリアル名「{name}」には、Windows-1252 '
'コードページではエンコードできない文字が含まれています',
('*', 'Material slot cannot be empty (index {index})'): 'マテリアルスロットを空にすることはできません '
'(index '
'{index})',
('*', 'Materials'): 'マテリアル',
('*', 'Metadata'): 'メタデータ',
('*', 'Modulate'): '変調する',
('*', 'Move the selected material down one slot'): '選択したマテリアルを 1 '
'スロット下に移動',
('*', 'Move the selected material up one slot'): '選択したマテリアルを 1 '
'つ上のスロットに移動',
('*', 'NLA Track'): 'NLA トラック',
('*', 'NLA Track Index'): 'NLA トラックインデックス',
('*', 'NLA Track Strips'): 'NLA トラックストリップ',
('*', 'No NLA track strips were selected for export'): 'エクスポート対象の NLA '
'トラックストリップが選択されていません',
('*', 'No Smooth'): 'スムーズなし',
('*', 'No actions were selected for export'): 'エクスポートするアクションが選択されていません',
('*', 'No animation data for object "{name}"'): 'オブジェクト「{name}」のアニメーションデータはありません',
('*', 'No bones available for export'): 'エクスポートできるボーンはありません',
('*', 'No modifiers will be evaluated as part of the exported mesh'): 'エクスポートされたメッシュの一部として評価されるモディファイヤはありません',
('*', 'No sequences selected'): 'シーケンスが選択されていません',
('*', 'No text block selected'): 'テキストブロックが選択されていません',
('*', 'No timeline markers were selected for export'): 'エクスポートするタイムラインマーカーが選択されていません',
('*', 'Normal Two-Sided'): 'ノーマル両面',
('*', 'Nothing to import'): 'インポートするものはありません',
('*', 'Only Show Selected'): '選択したものだけを表示',
('*', 'Only bones belonging to the selected bone collections and their ancestors will be exported'): '選択したボーンコレクションに属するボーンとその祖先だけがエクスポートされます',
('*', "Only show items matching this name (use '*' as wildcard)"): 'この名前に一致するアイテムのみを表示 '
'(ワイルドカードとして「*」を使用)',
('*', 'Only show selected sequences'): '選択したシーケンスのみを表示',
('*', 'Override Animation Data'): 'アニメーションデータをオーバーライドする',
('*', 'PSA Export'): 'PSA エクスポート',
('*', 'PSA export successful'): 'PSA のエクスポートが成功しました',
('*', 'PSK Material'): 'PSK マテリアル',
('*', 'PSK export successful'): 'PSK のエクスポートが成功しました',
('*', 'PSK imported ({name})'): 'PSK がインポートされました ({name})',
('*', 'PSK imported with {count} warning(s)'): 'PSK が {count} '
'という警告とともにインポートされました',
('*', 'PSK/PSA Import/Export (.psk/.psa)'): 'PSK/PSA インポート/エクスポート '
'(.psk/.psa)',
('*', 'PSK/PSA Importer/Exporter'): 'PSK/PSA インポーター/エクスポーター',
('*', 'Prefix Action Name'): 'プレフィックスアクション名',
('*', 'Raw Mesh Data'): '未加工メッシュデータ',
('*', 'Regular Expression'): '正規表現',
('*', 'RemoveTracks'): 'トラックを削除',
('*', 'Reversed'): '逆転しました',
('*', 'Root Motion'): 'ルートモーション',
('*', 'Select All'): 'すべて選択',
('*', 'Select By Text List'): 'テキストリストで選択', ('*', 'Select By Text List'): 'テキストリストで選択',
('*', 'Select a PSA file'): 'PSA ファイルを選択',
('*', 'Select all bone collections'): 'すべてのボーンコレクションを選択',
('*', 'Select all visible sequences'): '表示されているシーケンスをすべて選択',
('*', 'Select sequences by name from text list'): 'テキストリストから名前でシーケンスを選択', ('*', 'Select sequences by name from text list'): 'テキストリストから名前でシーケンスを選択',
('*', 'Bone Name Mapping'): 'ボーン名のマッピング', ('*', 'Selected object must be an Armature'): '選択したオブジェクトはアーマチュアでなければなりません',
('*', 'Selected {count} sequence(s)'): '選択した {count} シーケンス',
('*', 'Sequence'): 'シーケンス',
('*', 'Sequence name "{name}" contains characters that cannot be encoded in the Windows-1252 codepage'): 'シーケンス名「{name}」には、Windows-1252 '
'コードページではエンコードできない文字が含まれています',
('*', 'Sequences'): 'シーケンス',
('*', 'Sequences are delineated by scene timeline markers'): 'シーケンスはシーンのタイムラインマーカーによって描かれます',
('*', 'Sequences are delineated by the start & end times of strips on the selected NLA track'): 'シーケンスは、選択した '
'NLA '
'トラックのストリップの開始時間と終了時間によって示されます',
('*', 'Sequences will be exported using actions'): 'シーケンスはアクションを使用してエクスポートされます',
('*', 'Shape Keys'): 'シェイプキー',
('*', 'Show actions that belong to an asset library'): 'アセットライブラリに属するアクションを表示',
('*', 'Show reversed sequences'): '逆のシーケンスを表示',
('*', 'Source'): 'ソース',
('*', 'Stash each imported action as a strip on a new non-contributing NLA track'): 'インポートした各アクションを、コントリビューションされていない新しい '
'NLA '
'トラックにストリップとして保存する',
('*', 'The active object must be an armature'): 'アクティブオブジェクトはアーマチュアでなければなりません',
('*', 'The frame rate of the exported sequence'): 'エクスポートされたシーケンスのフレームレート',
('*', 'The frame rate to which the imported sequences will be resampled to'): 'インポートしたシーケンスをリサンプリングするときのフレームレート',
('*', "The frame rate will be determined by action's FPS property found in the PSA Export panel.\n\nIf the Sequence Source is Timeline Markers, the lowest value of all contributing actions will be used"): 'フレームレートは、PSA '
'Export '
'パネルにあるアクションの '
'FPS '
'プロパティによって決まります。\n'
'\n'
'シーケンスソースがタイムラインマーカーの場合、関連するすべてのアクションのうち最も低い値が使用されます',
('*', 'The keyframe sampling ratio of the exported sequence.\n\nA compression ratio of 1.0 will export all frames, while a compression ratio of 0.5 will export half of the frames'): 'エクスポートされたシーケンスのキーフレームサンプリング比。\n'
'\n'
'圧縮率が '
'1.0 '
'の場合はすべてのフレームがエクスポートされ、圧縮率が '
'0.5 '
'の場合はフレームの半分がエクスポートされます',
('*', 'The method by which bones from the PSA file are mapped to the bones of the armature'): 'PSA '
'ファイルのボーンをアーマチュアのボーンにマッピングする方法',
('*', 'The minimum number of keyframes to be exported'): 'エクスポートするキーフレームの最小数',
('*', 'The selected object must be an armature'): '選択したオブジェクトはアーマチュアでなければなりません',
('*', 'The sequence frame rate matches the original frame rate'): 'シーケンスのフレームレートは元のフレームレートと一致します',
('*', 'The sequence is resampled to a custom frame rate'): 'シーケンスはカスタムフレームレートにリサンプリングされます',
('*', 'The sequence is resampled to the frame rate of the scene'): 'シーケンスはシーンのフレームレートに合わせてリサンプリングされます',
('*', 'The source vertex color space'): 'ソース頂点カラースペース',
('*', 'Translucent'): '半透明',
('*', 'Triangle Bit Flags'): 'トライアングルビットフラグ',
('*', 'Triangle Type'): 'トライアングルタイプ',
('*', 'Unlit'): '明かりなし',
('*', 'Unreal PSA (.psa)'): 'アンリアルスパ (.psa)',
('*', 'Unreal PSK (.psk)'): 'アンリアルPSK (.psk)',
('*', 'Unreal PSK (.psk/.pskx)'): 'アンリアルPSK (.psk/.pskx)',
('*', 'Unrecognized wedge format'): '認識されないウェッジフォーマット',
('*', 'Use Config File'): '設定ファイルを使用', ('*', 'Use Config File'): '設定ファイルを使用',
} ('*', 'Use the .config file that is sometimes generated when the PSA file is exported from UEViewer. This file contains options that can be used to filter out certain bones tracks from the imported actions'): 'PSA '
} 'ファイルを '
'UEViewer '
'からエクスポートしたときに生成されることがある.config '
'ファイルを使用してください。このファイルには、インポートしたアクションから特定のボーントラックを除外するためのオプションが含まれています',
('*', 'Use the animation data from a different object instead of the selected object'): '選択したオブジェクトの代わりに別のオブジェクトのアニメーションデータを使用する',
('*', 'Vertex Color Space'): '頂点カラースペース',
('*', 'Vertex Colors'): '頂点カラー',
('*', 'Vertex Normals'): '頂点法線',
('*', 'When enabled, the root bone will be transformed as it appears in the scene.\n\nYou might want to disable this if you are exporting an animation for an armature that is attached to another object, such as a weapon or a shield'): '有効にすると、ルートボーンはシーンに表示されているとおりにトランスフォームされます。\n'
'\n'
'武器や盾など、別のオブジェクトにアタッチされているアーマチュアのアニメーションをエクスポートする場合は、これを無効にすると良いかもしれません',
('*', 'Write'): '書き込み',
('*', 'sRGBA'): 'sRGBA'}}

View File

@@ -1,11 +1,14 @@
import ast import ast
import glob import glob
import os import os
import pathlib
import polib
# Walk the directory and open all .py files using glob # Walk the directory and open all .py files using glob
strings = set() strings = dict()
root_path = pathlib.Path('../io_scene_psk_psa').resolve()
for file in glob.glob('../io_scene_psk_psa/**/*.py', recursive=True): for file in glob.glob('../io_scene_psk_psa/**/*.py', recursive=True):
print(file)
with open(os.path.join(file), 'r') as f: with open(os.path.join(file), 'r') as f:
if file.endswith('i18n.py'): if file.endswith('i18n.py'):
# TODO: Don't parse the i18n files. # TODO: Don't parse the i18n files.
@@ -15,37 +18,39 @@ for file in glob.glob('../io_scene_psk_psa/**/*.py', recursive=True):
a = ast.parse(f.read()) a = ast.parse(f.read())
for node in ast.walk(a): for node in ast.walk(a):
if isinstance(node, ast.Constant) and isinstance(node.value, str): if isinstance(node, ast.Constant) and isinstance(node.value, str):
strings.add(node.s) a = pathlib.Path(file).resolve()
filepath = a.relative_to(root_path)
print(filepath)
if node.s not in strings:
strings[node.s] = filepath, node.lineno, node.col_offset
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
print(f'Error reading file {file}: {e}') print(f'Error reading file {file}: {e}')
# Remove all strings that are empty or contain only whitespace. string_keys = set(strings.keys())
strings = set(filter(lambda x: x.strip(), strings))
# Remove all keys from the dictionary that are empty or contain only whitespace.
string_keys = set(filter(lambda x: x.strip(), string_keys))
# Remove all strings that have no alphabetic characters. # Remove all strings that have no alphabetic characters.
strings = set(filter(lambda x: any(c.isalpha() for c in x), strings)) string_keys = set(filter(lambda x: any(c.isalpha() for c in x), string_keys))
# Remove any strings that have '@return: ' in them. # Remove any strings that have '@return: ' in them.
strings = set(filter(lambda x: '@return: ' not in x, strings)) string_keys = set(filter(lambda x: '@return: ' not in x, string_keys))
# Remove any strings that are entirely lowercase and have no whitespace. # Remove any strings that are entirely lowercase and have no whitespace.
strings = set(filter(lambda x: not x.islower() or ' ' in x, strings)) string_keys = set(filter(lambda x: not x.islower() or ' ' in x, string_keys))
# Remove any strings that are in SCREAMING_SNAKE_CASE. # Remove any strings that are in SCREAMING_SNAKE_CASE.
strings = set(filter(lambda x: not x.isupper(), strings)) string_keys = set(filter(lambda x: not x.isupper(), string_keys))
# Remove any strings that have underscores. # Remove any strings that have underscores.
strings = set(filter(lambda x: '_' not in x, strings)) string_keys = set(filter(lambda x: '_' not in x, string_keys))
# Remove any string that starts with a newline. # Remove any string that starts with a newline.
strings = set(filter(lambda x: not x.startswith('\n'), strings)) string_keys = set(filter(lambda x: not x.startswith('\n'), string_keys))
# Remove any string that looks like a regular expression. # Remove any string that looks like a regular expression.
strings = set(filter(lambda x: not any(c in x for c in '^'), strings)) string_keys = set(filter(lambda x: not any(c in x for c in '^'), string_keys))
# Convert the set to a list and sort it.
strings = list(strings)
strings.sort()
def write_multiline_string(f, string): def write_multiline_string(f, string):
f.write(f'msgid ""\n') f.write(f'msgid ""\n')
@@ -84,7 +89,6 @@ exclude_strings = {
'Select', 'Select',
'RemoveTracks' 'RemoveTracks'
'Source', 'Source',
'Stash',
'Move Up', 'Move Up',
'Move Down', 'Move Down',
'Unassigned', 'Unassigned',
@@ -92,41 +96,38 @@ exclude_strings = {
'Suffix', 'Suffix',
'Timeline Markers', 'Timeline Markers',
'Pose Markers', 'Pose Markers',
'Actions' 'Actions',
'sRGBA',
} }
# Remove any strings that are in the exclude_strings set. # Remove any strings that are in the exclude_strings set.
strings = set(filter(lambda x: x not in exclude_strings, strings)) string_keys = set(filter(lambda x: x not in exclude_strings, string_keys))
with open('./artifacts/io_scene_psk_psa.en.po', 'w') as f: # Make a new PO file and write the strings to it.
# Write the header (language, mime-version, content-type & content-transfer-encoding). pofile = polib.POFile()
f.write('msgid ""\n'
'msgstr ""\n' pofile.header = '''msgid ""
'"Language: en\\n"\n' msgstr ""
'"MIME-Version: 1.0\\n"\n' "Language: en\\n"
'"Content-Type: text/plain\\n"\n' "MIME-Version: 1.0\\n"
'"Content-Transfer-Encoding: 8bit; charset=UTF-8\\n"\n\n' "Content-Type: text/plain; charset=UTF-8\\n"
) "Content-Transfer-Encoding: 8bit\\n"
for string in strings: '''
if is_multi_line := '\n' in string:
f.write(f'msgid ""\n') # Sort the string keys into a list.
# Split the string into lines and write each line as a separate msgid. string_keys = list(string_keys)
for line in string.split('\n'): string_keys.sort()
f.write(f'"{line}"\n')
f.write(f'msgstr ""\n') for string_key in string_keys:
# Split the string into lines and write each line as a separate msgid. file, line, col = strings[string_key]
for line in string.split('\n'): entry = polib.POEntry(
f.write(f'"{line}"\n') msgid=string_key,
else: msgstr=string_key,
f.write(f'msgid "{string}"\n') comment=f'{file}:{line}',
f.write(f'msgstr "{string}"\n') )
f.write('\n') pofile.append(entry)
pofile.save('../extern/io_scene_psk_psa-translations/io_scene_psk_psa.en.po')
# Print the # of strings. # Print the # of strings.
print(f'Found {len(strings)} strings.') print(f'Found {len(string_keys)} strings.')
# Zip the file.
import zipfile
with zipfile.ZipFile('./artifacts/io_scene_psk_psa.po.zip', 'w') as z:
z.write('./artifacts/io_scene_psk_psa.en.po')

View File

@@ -0,0 +1,27 @@
import os.path
import pprint
import re
from glob import glob
import polib
langs = {}
for file_path in glob('../extern/io_scene_psk_psa-translations/io_scene_psk_psa.*.po'):
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
po = polib.pofile(content)
# Get the language code from the file name.
lang_code = re.match(r'io_scene_psk_psa.(\w*)\.po', os.path.basename(file_path)).group(1)
if lang_code == 'en':
continue
langs[lang_code] = {('*', entry.msgid): entry.msgstr for entry in po if entry.msgid != ''}
with open('../io_scene_psk_psa/i18n.py', 'w', encoding='utf-8') as f:
s = pprint.pformat(langs)
f.write(f'langs = {s}')
print(f'Language_codes = {list(langs.keys())}')
print('Wrote i18n.py')